#include "U2ColourValue.h"

#include "U2Math.h"


U2EG_NAMESPACE_USING


const U2ColourValue U2ColourValue::ZERO     = U2ColourValue(0.0,0.0,0.0,0.0);
const U2ColourValue U2ColourValue::Black    = U2ColourValue(0.0,0.0,0.0);
const U2ColourValue U2ColourValue::White    = U2ColourValue(1.0,1.0,1.0);
const U2ColourValue U2ColourValue::Red      = U2ColourValue(1.0,0.0,0.0);
const U2ColourValue U2ColourValue::Green    = U2ColourValue(0.0,1.0,0.0);
const U2ColourValue U2ColourValue::Blue     = U2ColourValue(0.0,0.0,1.0);

//---------------------------------------------------------------------
#if U2_ENDIAN == U2_ENDIAN_BIG
ABGR U2ColourValue::getAsABGR(void) const
#else
RGBA U2ColourValue::getAsRGBA(void) const
#endif
{
    u2uint8 val8;
    u2uint32 val32 = 0;

    // Convert to 32bit pattern
    // (RGBA = 8888)

    // Red
    val8 = static_cast<u2uint8>(r * 255);
    val32 = val8 << 24;

    // Green
    val8 = static_cast<u2uint8>(g * 255);
    val32 += val8 << 16;

    // Blue
    val8 = static_cast<u2uint8>(b * 255);
    val32 += val8 << 8;

    // Alpha
    val8 = static_cast<u2uint8>(a * 255);
    val32 += val8;

    return val32;
}
//---------------------------------------------------------------------
#if U2_ENDIAN == U2_ENDIAN_BIG
BGRA U2ColourValue::getAsBGRA(void) const
#else
ARGB U2ColourValue::getAsARGB(void) const
#endif
{
    u2uint8 val8;
    u2uint32 val32 = 0;

    // Convert to 32bit pattern
    // (ARGB = 8888)

    // Alpha
    val8 = static_cast<u2uint8>(a * 255);
    val32 = val8 << 24;

    // Red
    val8 = static_cast<u2uint8>(r * 255);
    val32 += val8 << 16;

    // Green
    val8 = static_cast<u2uint8>(g * 255);
    val32 += val8 << 8;

    // Blue
    val8 = static_cast<u2uint8>(b * 255);
    val32 += val8;


    return val32;
}
//---------------------------------------------------------------------
#if U2_ENDIAN == U2_ENDIAN_BIG
ARGB U2ColourValue::getAsARGB(void) const
#else
BGRA U2ColourValue::getAsBGRA(void) const
#endif
{
    u2uint8 val8;
    u2uint32 val32 = 0;

    // Convert to 32bit pattern
    // (ARGB = 8888)

    // Blue
    val8 = static_cast<u2uint8>(b * 255);
    val32 = val8 << 24;

    // Green
    val8 = static_cast<u2uint8>(g * 255);
    val32 += val8 << 16;

    // Red
    val8 = static_cast<u2uint8>(r * 255);
    val32 += val8 << 8;

    // Alpha
    val8 = static_cast<u2uint8>(a * 255);
    val32 += val8;


    return val32;
}
//---------------------------------------------------------------------
#if U2_ENDIAN == U2_ENDIAN_BIG
RGBA U2ColourValue::getAsRGBA(void) const
#else
ABGR U2ColourValue::getAsABGR(void) const
#endif
{
    u2uint8 val8;
    u2uint32 val32 = 0;

    // Convert to 32bit pattern
    // (ABRG = 8888)

    // Alpha
    val8 = static_cast<u2uint8>(a * 255);
    val32 = val8 << 24;

    // Blue
    val8 = static_cast<u2uint8>(b * 255);
    val32 += val8 << 16;

    // Green
    val8 = static_cast<u2uint8>(g * 255);
    val32 += val8 << 8;

    // Red
    val8 = static_cast<u2uint8>(r * 255);
    val32 += val8;


    return val32;
}
//---------------------------------------------------------------------
#if U2_ENDIAN == U2_ENDIAN_BIG
void U2ColourValue::setAsABGR(const ABGR val)
#else
void U2ColourValue::setAsRGBA(const RGBA val)
#endif
{
    u2uint32 val32 = val;

    // Convert from 32bit pattern
    // (RGBA = 8888)

    // Red
    r = ((val32 >> 24) & 0xFF) / 255.0f;

    // Green
    g = ((val32 >> 16) & 0xFF) / 255.0f;

    // Blue
    b = ((val32 >> 8) & 0xFF) / 255.0f;

    // Alpha
    a = (val32 & 0xFF) / 255.0f;
}
//---------------------------------------------------------------------
#if U2_ENDIAN == U2_ENDIAN_BIG
void U2ColourValue::setAsBGRA(const BGRA val)
#else
void U2ColourValue::setAsARGB(const ARGB val)
#endif
{
    u2uint32 val32 = val;

    // Convert from 32bit pattern
    // (ARGB = 8888)

    // Alpha
    a = ((val32 >> 24) & 0xFF) / 255.0f;

    // Red
    r = ((val32 >> 16) & 0xFF) / 255.0f;

    // Green
    g = ((val32 >> 8) & 0xFF) / 255.0f;

    // Blue
    b = (val32 & 0xFF) / 255.0f;
}
//---------------------------------------------------------------------
#if U2_ENDIAN == U2_ENDIAN_BIG
void U2ColourValue::setAsARGB(const ARGB val)
#else
void U2ColourValue::setAsBGRA(const BGRA val)
#endif
{
    u2uint32 val32 = val;

    // Convert from 32bit pattern
    // (ARGB = 8888)

    // Blue
    b = ((val32 >> 24) & 0xFF) / 255.0f;

    // Green
    g = ((val32 >> 16) & 0xFF) / 255.0f;

    // Red
    r = ((val32 >> 8) & 0xFF) / 255.0f;

    // Alpha
    a = (val32 & 0xFF) / 255.0f;
}
//---------------------------------------------------------------------
#if U2_ENDIAN == U2_ENDIAN_BIG
void U2ColourValue::setAsRGBA(const RGBA val)
#else
void U2ColourValue::setAsABGR(const ABGR val)
#endif
{
    u2uint32 val32 = val;

    // Convert from 32bit pattern
    // (ABGR = 8888)

    // Alpha
    a = ((val32 >> 24) & 0xFF) / 255.0f;

    // Blue
    b = ((val32 >> 16) & 0xFF) / 255.0f;

    // Green
    g = ((val32 >> 8) & 0xFF) / 255.0f;

    // Red
    r = (val32 & 0xFF) / 255.0f;
}
//---------------------------------------------------------------------
bool U2ColourValue::operator==(const U2ColourValue& rhs) const
{
    return (r == rhs.r &&
        g == rhs.g &&
        b == rhs.b &&
        a == rhs.a);
}
//---------------------------------------------------------------------
bool U2ColourValue::operator!=(const U2ColourValue& rhs) const
{
    return !(*this == rhs);
}
//---------------------------------------------------------------------
void U2ColourValue::setHSB(u2real hue, u2real saturation, u2real brightness)
{
    // wrap hue
    if (hue > 1.0f)
    {
        hue -= (int)hue;
    }
    else if (hue < 0.0f)
    {
        hue += (int)hue + 1;
    }
    // clamp saturation / brightness
    saturation = std::min(saturation, (u2real)1.0);
    saturation = std::max(saturation, (u2real)0.0);
    brightness = std::min(brightness, (u2real)1.0);
    brightness = std::max(brightness, (u2real)0.0);

    if (brightness == 0.0f)
    {   
        // early exit, this has to be black
        r = g = b = 0.0f;
        return;
    }

    if (saturation == 0.0f)
    {   
        // early exit, this has to be grey

        r = g = b = brightness;
        return;
    }


    u2real hueDomain  = hue * 6.0f;
    if (hueDomain >= 6.0f)
    {
        // wrap around, and allow mathematical errors
        hueDomain = 0.0f;
    }
    unsigned short domain = (unsigned short)hueDomain;
    u2real f1 = brightness * (1 - saturation);
    u2real f2 = brightness * (1 - saturation * (hueDomain - domain));
    u2real f3 = brightness * (1 - saturation * (1 - (hueDomain - domain)));

    switch (domain)
    {
    case 0:
        // red domain; green ascends
        r = brightness;
        g = f3;
        b = f1;
        break;
    case 1:
        // yellow domain; red descends
        r = f2;
        g = brightness;
        b = f1;
        break;
    case 2:
        // green domain; blue ascends
        r = f1;
        g = brightness;
        b = f3;
        break;
    case 3:
        // cyan domain; green descends
        r = f1;
        g = f2;
        b = brightness;
        break;
    case 4:
        // blue domain; red ascends
        r = f3;
        g = f1;
        b = brightness;
        break;
    case 5:
        // magenta domain; blue descends
        r = brightness;
        g = f1;
        b = f2;
        break;
    }


}
//---------------------------------------------------------------------
void U2ColourValue::getHSB(u2real* hue, u2real* saturation, u2real* brightness) const
{

    u2real vMin = std::min(r, std::min(g, b));
    u2real vMax = std::max(r, std::max(g, b));
    u2real delta = vMax - vMin;

    *brightness = vMax;

    if (U2Math::RealEqual(delta, 0.0f, 1e-6))
    {
        // grey
        *hue = 0;
        *saturation = 0;
    }
    else                                    
    {
        // a colour
        *saturation = delta / vMax;

        u2real deltaR = (((vMax - r) / 6.0f) + (delta / 2.0f)) / delta;
        u2real deltaG = (((vMax - g) / 6.0f) + (delta / 2.0f)) / delta;
        u2real deltaB = (((vMax - b) / 6.0f) + (delta / 2.0f)) / delta;

        if (U2Math::RealEqual(r, vMax))
            *hue = deltaB - deltaG;
        else if (U2Math::RealEqual(g, vMax))
            *hue = 0.3333333f + deltaR - deltaB;
        else if (U2Math::RealEqual(b, vMax)) 
            *hue = 0.6666667f + deltaG - deltaR;

        if (*hue < 0.0f) 
            *hue += 1.0f;
        if (*hue > 1.0f)
            *hue -= 1.0f;
    }


}
